不知道大家有沒有買 Switch 呢?
雖然兔兔沒有 Switch,
但我知道有遊戲片都不便宜呀!
然後,遊戲總會有玩膩的時候
這時候就要把記憶卡插槽裡的卡片拿出來,
接著插一張新的
進去。
不過其實,
你不知道有沒有發現,
我們自定義的元件都是只有標籤,
都沒在裡面放內容呢?
<Box :number="1" />
<Box :number="2">
</Box>
我們目前都是把所有的資料丟在屬性,
可是其實原生的寫法應該要可以做到這樣:
<Box>1</Box>
<Box>2</Box>
但你會發現在自定義的元件之中這樣用是沒反應的
。
WHY?
我們接下來就來談談為什麼!
如果要像前面所說,想要可以跟原生的寫法一樣能夠在裡面加上內容的話,Slot
就是你要找的功能!
不過我們就要解釋一下為何得靠其他功能來解決,而不能直接放在裡面。
讓我們先看看元件實際上的意義:
<!-- 看起來的樣子 -->
<Menu />
<!-- 實際上的樣子 -->
<ul>
<li>首頁</li>
<li>關於</li>
<li>賣場</li>
<li>服務</li>
</ul>
沒錯,就是做結構簡化
和整理
。
那麼如果今天在元件內加入內容呢?
<!-- 看起來的樣子 -->
<Menu>
這是我們的選單。
</Menu>
<!-- 實際上的樣子 -->
<ul>
<li>首頁</li>
<li>關於</li>
<li>賣場</li>
<li>服務</li>
</ul>
沒反應,因為他不知道你要把這些內容實際上是要安排在 template 中的哪裡!
沒錯,就是這麼簡單的問題。
所以 slot 的功能就是要用來告訴 vue 我們要把內容插在
實際上結構的哪個地方
。
而其實 slot 的用法很單純又簡單,
我們只要加上 <slot></slot>
就好。
舉例:
<!-- Menu.vue -->
<template>
<ul>
<slot></slot>
<li v-for="link in links">{{ link }}</li>
</ul>
</template>
<script>
export default {
data() {
return {
links: ["首頁","關於","賣場","服務"]
}
}
}
</script>
那麼效果就會是這樣:
<!-- 看起來的樣子 -->
<Menu>
這是我們的選單。
</Menu>
<!-- 實際上的樣子 -->
<ul>
這是我們的選單。
<li>首頁</li>
<li>關於</li>
<li>賣場</li>
<li>服務</li>
</ul>
是不是很輕鬆簡單?
但你可能會納悶:
「目前這個效果用 props 也能做到啊!」
噢,沒錯,你說的對。
不過這樣呢?
<!-- 看起來的樣子 -->
<Menu>
<div>
<img src="banner.jpg" />
<span>歡迎光臨 myfone</span>
</div>
</Menu>
<!-- 實際上的樣子 -->
<ul>
<div>
<img src="banner.jpg" />
<span>歡迎光臨 myfone</span>
</div>
<li>首頁</li>
<li>關於</li>
<li>賣場</li>
<li>服務</li>
</ul>
如果今天要拋入的是元素
,是不是就很困難啦?
但是使用 slot 就簡單太多啦!
其實應該也沒有很明確的使用時機,
不過多半會用在需要替換內容
的元件,比如 modal。
而有時候使用他是為了架構清晰
,保持與視覺一致
。
還有一點我覺得很重要的,
就是如果你要 跨祖孫元件交換資料
的時候,
可以更方便。
因為這麼做等於平坦化,
原本要祖孫元件溝通,現在可以降維成父子元件溝通。
舉例:
<!-- 主畫面 -->
<div>
<List @modify="change($event)" />
</div>
<!-- List.vue -->
<div>
<div>標題</div>
<ul>
<Item v-for="i in 10" @modify="$emit('modify', $event)" />
</ul>
</div>
<!-- Item.vue -->
<li>
<input type="text" />
<button @click="$emit('modify', text)">修改</button>
</li>
像上述這樣的祖孫結構,
資料傳遞起來就很麻煩。
因為你 props 跟 emit 可能都需要連傳兩層。
但為何說 slot 可以平坦化、降成父子關係?
看了就明白了:
<!-- 主畫面 -->
<div>
<List>
<Item v-for="i in 10" @modify="$emit('modify', $event)" />
</List>
</div>
<!-- List.vue -->
<div>
<div>標題</div>
<ul>
<slot>
目前沒有任何文章。
</slot>
</ul>
</div>
<!-- Item.vue -->
<li>
<input type="text" />
<button @click="$emit('modify', text)">修改</button>
</li>
順帶一提,slot 是可以給予預設內容的,在沒有從外部填入內容之前,可以維持顯示預設內容。
這樣我在主畫面之中就可以直接對 Item 元件存取了,
而且結構上看起來可讀性比較高。
不過這種用法還是要經過考慮和評估,
因為如果太濫用,等於所有資料都得跑在父層上,
太混雜了,肯定維護起來不容易。
(就跟以不好的觀念使用 vuex 一樣,很可怕。)
其實不只可以有一個插槽,也可以有 N 個。
只是,結構裡面就一個區域啊,
怎麼知道我要指定插到哪個插槽?
這時候就是具名插槽
該登場的時候啦~
具名插槽也很簡單,就是這樣定義:
<!-- Menu.vue -->
<template>
<ul>
<slot name="title"></slot>
<li v-for="link in links">{{ link }}</li>
</ul>
<slot name="description"></slot>
</template>
<script>
export default {
data() {
return {
links: ["首頁","關於","賣場","服務"]
}
}
}
</script>
對,簡單的加上 name,然後使用時只要像 v-on:
、v-bind:
那類的語法一般,加上 v-slot:
以及名稱。
不過必須用 <template></template>
將區塊包裹起來。
所以就會是這樣:
<!-- 使用時看起來的樣子 -->
<Menu>
<template v-slot:title>
這是我們的選單。
</template>
<template v-slot:description>
選單項目將會不定時更新,敬請期待。
</template>
</Menu>
<!-- 實際上的樣子 -->
<ul>
這是我們的選單。
<li>首頁</li>
<li>關於</li>
<li>賣場</li>
<li>服務</li>
</ul>
選單項目將會不定時更新,敬請期待。
什麼? 你期待這個也有短語法
?
我說你啊,人不能這麼懶,
而且這種功能要什麼短...
等等,還真的有!
試著用 #
來替代 v-slot
當縮短語法:
<Menu>
<template #title>
這是我們的選單。
</template>
<template #description>
選單項目將會不定時更新,敬請期待。
</template>
</Menu>
嘿,丟啦!
這樣就可以了。
「兔兔! 元件放到插槽之後,就沒辦法吃到前一層元件的變數了欸,那這樣怎麼辦!」
哈哈哈哈哈!
當然不會有這麼不方便的事情存在啊!
其實插槽是有 Props
的!
我們來看看該如何幫插槽設置 props:
<!-- Menu.vue -->
<template>
<div>
{{ title }}
</div>
<div>
<ul>
<slot :list="links"></slot>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
title: "選單",
links: ["首頁","關於","賣場","服務"]
}
}
}
</script>
就跟一般平時你給 props 的方式一模一樣!
不過存取的方式不一樣:
<Menu v-slot="slotProps">
<li v-for="item in slotProps.list">
{{ item }}
</li>
</Menu>
要透過在 v-slot
後加上 ="slotProps"
。
slotProps 是 slot 預設的 props 組名稱,傳遞過來的 props 都是 slotProps 下的一個屬性。
當然我們也能解構:
<Menu v-slot="{ list }">
<li v-for="item in list">
{{ item }}
</li>
</Menu>
這樣能存取到上層的變數,是不是又更方便了呢!
(雖然我想不太到使用時機)
哇! slot 就這麼介紹了!
其實還有一些更細部的功能啦,
但那些應該要自己去閱讀文件,
我的 vue 篇主要是為了,
把我們之後做元件會用到的功能都先帶過一遍。
那其實 vue 篇就這樣結束啦!(幫 vue 篇完結灑花)
我們後面接著就要來開始實作元件了!
好期待,不知道大家會不會覺得好玩呢?
關於兔兔們:
( # 兔兔小聲說 )
在你們的國度,駕照都是用雞腿換的。
在兔兔這裡,你們知道用的是什麼嗎?
「紅蘿蔔!」
嘖嘖嘖,你以為紅蘿蔔在我這裡這麼好價嘛!
那可是相當於米飯的存在欸!來,湊近點,我告訴你答案。
其實是:黃根!!